﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using DevWebServer.Test.Properties;
using MC2Development.DevWebServer;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace DevWebServer.Test
{
    /// <summary>
    ///This is a test class for WebServerTest and is intended
    ///to contain all WebServerTest Unit Tests
    ///</summary>
    [TestClass()]
    public class WebServerTest
    {
        #region Constants

        private const string EXPECTED_EXCEPTION_NOT_THROWN = "The expected exception was not thrown";

        #endregion

        #region Private Members

        private TestContext m_testContextInstance;

        #endregion

        #region Test Context

        /// <summary>
        ///Gets or sets the test context which provides
        ///information about and functionality for the current test run.
        ///</summary>
        public TestContext TestContext
        {
            get
            {
                return m_testContextInstance;
            }
            set
            {
                m_testContextInstance = value;
            }
        }

        #endregion

        #region Additional test attributes
        // 
        //You can use the following additional attributes as you write your tests:
        //
        //Use ClassInitialize to run code before running the first test in the class
        //[ClassInitialize()]
        //public static void MyClassInitialize(TestContext testContext)
        //{
        //}
        //
        //Use ClassCleanup to run code after all tests in a class have run
        //[ClassCleanup()]
        //public static void MyClassCleanup()
        //{
        //}
        //
        //Use TestInitialize to run code before running each test
        //[TestInitialize()]
        //public void MyTestInitialize()
        //{
        //}
        //
        //Use TestCleanup to run code after each test has run
        //[TestCleanup()]
        //public void MyTestCleanup()
        //{
        //}
        //
        #endregion

        #region Test Methods

        /// <summary>
        ///A test for WebServer Constructor
        ///</summary>
        [TestMethod()]
        [ExpectedException(typeof(ArgumentNullException))]
        public void WebServerConstructorEmptyTest()
        {
            WebServer target = new WebServer(String.Empty);
        }

        /// <summary>
        ///A test for WebServer with the minimal settings
        ///</summary>
        [TestMethod()]
        public void WebServerMinimalTest()
        {
            const String TEST_BASE_URL = "http://localhost:8001/";

            using (WebServer target = new WebServer(TEST_BASE_URL))
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(TEST_BASE_URL);
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            }
        }

        /// <summary>
        ///A test for WebServer with the default Folder type handler
        ///</summary>
        [TestMethod()]
        public void WebServerContentPathTest()
        {
            const string TEST_BASE_URL = "http://localhost:8001/";
            const string CONTENT_TYPE = "text/html";

            ServerParameters serverParameters = new ServerParameters()
            {
                BaseAddresses = new List<Uri>() { new Uri(TEST_BASE_URL) },
                RequestHandler = new Handler()
                {
                    HandlerType = HandlerType.Folder,
                    ContentPath = GetResponseFilesPath() 
                },
                ContentType = CONTENT_TYPE
            };

            using (WebServer target = new WebServer(serverParameters))
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(TEST_BASE_URL + "test.html");
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
                Assert.AreEqual(CONTENT_TYPE, response.ContentType);

                using (StreamReader sr = new StreamReader(response.GetResponseStream()))
                {
                    String strResponse = sr.ReadToEnd();
                    Trace.WriteLine(strResponse);
                    Assert.IsTrue(strResponse.Contains("<title>Test</title>"), "Response title not as expected");
                    Assert.IsTrue(strResponse.Contains("<h1>Test</h1>"), "Response content not as expected");
                }
            }
        }

        /// <summary>
        ///A test for WebServer for a not found response
        ///</summary>
        [TestMethod()]
        public void WebServerNotFoundTest()
        {
            const String TEST_BASE_URL = "http://localhost:8001/";

            ServerParameters serverParameters = new ServerParameters()
            {
                BaseAddresses = new List<Uri>() { new Uri(TEST_BASE_URL) },
                RequestHandler = new Handler()
                {
                    HandlerType = HandlerType.Folder,
                    ContentPath = GetResponseFilesPath()
                }
            };

            using (WebServer target = new WebServer(serverParameters))
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(TEST_BASE_URL + "notfound.html");
                HttpWebResponse response = null;
                try
                {
                    response = (HttpWebResponse)request.GetResponse();
                    Assert.Fail(EXPECTED_EXCEPTION_NOT_THROWN);
                }
                catch (WebException ex)
                {
                    response = ex.Response as HttpWebResponse;
                    Assert.AreEqual(HttpStatusCode.NotFound, response.StatusCode);
                }
            }
        }

        /// <summary>
        ///A test for WebServer for an authentication failure
        ///</summary>
        [TestMethod()]
        public void WebServerAuthenticationFailureTest()
        {
            const String TEST_BASE_URL = "http://localhost:8001/";

            ServerParameters serverParameters = new ServerParameters()
            {
                BaseAddresses = new List<Uri>() { new Uri(TEST_BASE_URL) },
                RequestHandler = new Handler()
                {
                    HandlerType = HandlerType.Folder,
                    ContentPath = GetResponseFilesPath()
                },
                Authentication = new Authentication()
                {
                    AuthenticationType = AuthenticationType.Basic,
                    AuthenticationCredentials = new Credentials()
                    {
                        Username = "username",
                        Password = "pwd"
                    }
                }
            };

            using (WebServer target = new WebServer(serverParameters))
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(TEST_BASE_URL + "test.html");
                HttpWebResponse response = null;
                try
                {
                    response = (HttpWebResponse)request.GetResponse();
                    Assert.Fail(EXPECTED_EXCEPTION_NOT_THROWN);
                }
                catch (WebException ex)
                {
                    response = ex.Response as HttpWebResponse;
                    Assert.AreEqual(HttpStatusCode.Forbidden, response.StatusCode);
                }
            }
        }

        /// <summary>
        ///A test for WebServer for an authentication success
        ///</summary>
        [TestMethod()]
        public void WebServerAuthenticationSuccessTest()
        {
            const String TEST_BASE_URL = "http://localhost:8001/";

            ServerParameters serverParameters = new ServerParameters()
            {
                BaseAddresses = new List<Uri>() { new Uri(TEST_BASE_URL) },
                RequestHandler = new Handler()
                {
                    HandlerType = HandlerType.Folder,
                    ContentPath = GetResponseFilesPath()
                },
                Authentication = new Authentication()
                {
                    AuthenticationType = AuthenticationType.Basic,
                    AuthenticationCredentials = new Credentials()
                    {
                        Username = "username",
                        Password = "pwd"
                    }
                }
            };

            using (WebServer target = new WebServer(serverParameters))
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(TEST_BASE_URL + "test.html");
                SetBasicAuthHeader(request, "username", "pwd");

                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            }
        }

        /// <summary>
        ///A test for WebServer with a custom authenticator
        ///</summary>
        [TestMethod()]
        public void WebServerCustomAuthenticatorTest()
        {
            const String TEST_BASE_URL = "http://localhost:8001/";

            ServerParameters serverParameters = new ServerParameters()
            {
                BaseAddresses = new List<Uri>() { new Uri(TEST_BASE_URL) },
                RequestHandler = new Handler()
                {
                    HandlerType = HandlerType.Folder,
                    ContentPath = GetResponseFilesPath()
                },
                Authentication = new Authentication()
                {
                    CustomAuthenticator = new ConfigAuthenticator(),
                    AuthenticationType = AuthenticationType.Basic
                }
            };

            using (WebServer target = new WebServer(serverParameters))
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(TEST_BASE_URL + "test.html");
                SetBasicAuthHeader(request, "Mickey", "Mouse");

                HttpWebResponse response = null;
                response = (HttpWebResponse)request.GetResponse();
                Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
            }
        }

        /// <summary>
        ///A test for WebServer with a custom response handler
        ///</summary>
        [TestMethod()]
        public void WebServerResponseHandlerTest()
        {
            const String TEST_BASE_URL = "http://localhost:8001/";

            ServerParameters serverParameters = new ServerParameters()
            {
                BaseAddresses = new List<Uri>() { new Uri(TEST_BASE_URL) },
                RequestHandler = new Handler()
                {
                    HandlerType = HandlerType.Delegate,
                    RequestHandlerAction = AcceptedRequestHandler
                }
            };

            using (WebServer target = new WebServer(serverParameters))
            {
                HttpWebRequest request = (HttpWebRequest)WebRequest.Create(TEST_BASE_URL + "page.htm");
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                Assert.AreEqual(HttpStatusCode.Accepted, response.StatusCode);
            }

        }

        /// <summary>
        ///A test for WebServer with a timeout
        ///</summary>
        [TestMethod()]
        public void WebServerTimeoutTest()
        {
            const String TEST_BASE_URL = "http://localhost:8001/";

            ServerParameters serverParameters = new ServerParameters()
            {
                BaseAddresses = new List<Uri>() { new Uri(TEST_BASE_URL) },
                RequestHandler = new Handler()
                {
                    HandlerType = HandlerType.Folder,
                    ContentPath = GetResponseFilesPath()
                },
                Delay = 2000
            };

            try
            {
                using (WebServer target = new WebServer(serverParameters))
                {
                    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(TEST_BASE_URL + "page.htm");
                    request.Timeout = 500;
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    Assert.AreEqual(HttpStatusCode.Accepted, response.StatusCode);
                }

                Assert.Fail(EXPECTED_EXCEPTION_NOT_THROWN);
            }
            catch (WebException ex)
            {
                Assert.IsTrue(ex.Message.Contains("The operation has timed out"));
            }
        }

        #endregion

        #region Private Methods

        private string GetResponseFilesPath()
        {
            return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, Resources.ResponseFilesPath);
        }

        private void SetBasicAuthHeader(WebRequest req, String userName, String userPassword)
        {
            string authInfo = userName + ":" + userPassword;
            authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
            req.Headers["Authorization"] = "Basic " + authInfo;
        }

        #endregion

        #region Response Handlers

        public void AcceptedRequestHandler(HttpListenerContext context)
        {
            context.Response.StatusCode = (int)HttpStatusCode.Accepted;
            context.Response.OutputStream.Close();
        }

        #endregion
    }
}
